﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace LightCAD.Runtime
{
    public class DictionaryArray<T> : IEnumerable, IListPosition
    {
        private const int _defaultCapacity = 0x10;
        private T[] _items;
        private int _size;
        private Dictionary<T, int> mSwapDictionary;
        private int pos;

        /// <summary>
        /// The default constractor which initializes the default capacity of the collection to 16.
        /// </summary>
        public DictionaryArray()
        {
            this._items = new T[0x10];
        }

        /// <summary>
        /// Initializes the vdArray object with a given capacity.
        /// </summary>
        /// <param name="capacity">An integer representing the preallocated memory size of the collection.</param>
        public DictionaryArray(int capacity)
        {
            if (capacity < 0)
            {
                throw new ArgumentOutOfRangeException("capacity  vdArray:capacity < 0");
            }
            this._items = new T[capacity];
            this._size = 0;
        }

        /// <summary>
        /// Adds an object to the end of the collection.
        /// </summary>
        /// <param name="value">An object to be added to the collection.</param>
        /// <returns>True if the operation was succesfull.</returns>
        public virtual bool AddItem(T value)
        {
            if (this._size == this._items.Length)
            {
                this.EnsureCapacity(this._size + 1);
            }
            this._items[this._size] = value;
            this._size++;
            return true;
        }

        /// <summary>
        /// Adds the objects of one list to the end of this list.
        /// </summary>
        /// <param name="c">A vdArray collection to be added to this list.</param>
        public virtual void AddRange(DictionaryArray<T> c)
        {
            this.InsertRange(this._size, c);
        }

        /// <summary>
        /// Get the object before the last object of the collection.
        /// </summary>
        /// <returns></returns>
        public T BeforeLast()
        {
            if (this.Count < 2)
            {
                return default(T);
            }
            return this[this.Count - 2];
        }

        /// <summary>
        /// Places an existing object of the collection to the beginning or the end of the list.
        /// </summary>
        /// <param name="Object">The object to be placed.</param>
        /// <param name="ToBack">If this value is true then the object will be placed to the end of the list else it will be placed to the beginning.</param>
        public void ChangeOrder(T Object, bool ToBack)
        {
            if ((this._size > 1) && this.RemoveItem(Object))
            {
                this._size++;
                this.EnsureCapacity(this._size);
                if (ToBack)
                {
                    Array.Copy(this._items, 0, this._items, 1, this._size - 1);
                    this._items[0] = Object;
                }
                else
                {
                    this._items[this._size - 1] = Object;
                }
            }
        }

        /// <summary>
        /// Internally used to speed up Swap method.
        /// </summary>
        public void ClearIndexDictionary()
        {
            this.mSwapDictionary = null;
        }

        /// <summary>
        /// Copies all objects from a given array to the collection.
        /// </summary>
        /// <param name="_array">An array object to retrieve it's objects.</param>
        public void CopyFrom(object _array)
        {
            if (_array != null)
            {
                if (!(_array is Array))
                {
                    throw new Exception("Incorect aguments in CopyFrom method");
                }
                this.RemoveAll();
                Array array = (Array)_array;
                this.EnsureCapacity(this._size + array.Length);
                array.CopyTo(this._items, 0);
                this._size = array.Length;
            }
        }

        /// <summary>
        /// Copies the collection's object to a specified array starting from a specified index.
        /// </summary>
        /// <param name="array">The array where the collection's objects will be copied.</param>
        /// <param name="index">The zero-based index in array at which copying begins.</param>
        public void CopyTo(Array array, int index)
        {
            foreach (T local in this._items)
            {
                if (index >= array.Length)
                {
                    return;
                }
                array.SetValue(local, index);
                index++;
            }
        }

        ///// <summary>
        ///// This Function is called foreach field name of the vdArray object when opening in vdml format.
        ///// </summary>
        ///// <param name="deserializer">The DeSerializer object.</param>
        ///// <param name="fieldname">The name of the property of the object.</param>
        ///// <param name="value">the value of the property.</param>
        ///// <returns>Returns False if the fieldname does not correspond to a property of the object.</returns>
        ///// <seealso cref="T:VectorDraw.Serialize.DeSerializer" />
        //public bool DeSerialize(DeSerializer deserializer, string fieldname, object value)
        //{
        //    if (fieldname == "_Count")
        //    {
        //        this.Capacity = Math.Max(this.Capacity, (int)value);
        //    }
        //    else if ((fieldname == "_Item") && (value != null))
        //    {
        //        this.AddItem((T)value);
        //    }
        //    else
        //    {
        //        return false;
        //    }
        //    return true;
        //}

        /// <summary>
        /// Changes the capacity of the collection to the given value if the existing capacity is less than this value.
        /// </summary>
        /// <param name="min">A value representing the new capacity of the collection.</param>
        public void EnsureCapacity(int min)
        {
            if (this._items.Length < min)
            {
                int num = (this._items.Length == 0) ? 0x10 : (this._items.Length * 2);
                if (num < min)
                {
                    num = min;
                }
                this.Capacity = num;
            }
        }

        /// <summary>
        /// Implements the "foreach" expression for the collection.
        /// </summary>
        /// <returns></returns>
        public IEnumerator GetEnumerator()
        {
            return new Enumerator((DictionaryArray<T>)this);
        }

        /// <summary>
        /// Find the index where a specified object is located in the list.
        /// </summary>
        /// <param name="Object">The object to find.</param>
        /// <returns>Returns the index where the object is or -1 if the object is not in the list.</returns>
        public int GetObjectRealPosition(T Object)
        {
            if (Object != null)
            {
                if (this.mSwapDictionary != null)
                {
                    if (this.mSwapDictionary.ContainsKey(Object))
                    {
                        return this.mSwapDictionary[Object];
                    }
                    return -1;
                }
                int num = 0;
                foreach (T local in this)
                {
                    if (object.ReferenceEquals(local, Object))
                    {
                        return num;
                    }
                    num++;
                }
            }
            return -1;
        }

        /// <summary>
        /// Insert an object at a specified index.
        /// </summary>
        /// <param name="index">The index where the object will be inserted.</param>
        /// <param name="Object">The object to be inserted.</param>
        public void InsertAt(int index, T Object)
        {
            if (index == this.Count)
            {
                this.AddItem(Object);
            }
            else
            {
                if (!this.IsValidIndex(index))
                {
                    throw new ArgumentOutOfRangeException("vdCollection:((index < 0) || (index >= this._size))");
                }
                this.EnsureCapacity(this._size + 1);
                Array.Copy(this._items, index, this._items, index + 1, this._size - index);
                this._size++;
                this._items[index] = Object;
            }
        }

        /// <summary>
        /// Inserts the object of a specified collection to a specified index of this list.
        /// </summary>
        /// <param name="index">An index representing the position where the objects will be inserted.</param>
        /// <param name="c">A vdArray collection to be inserted in this list.</param>
        public void InsertRange(int index, DictionaryArray<T> c)
        {
            int count = c.Count;
            if (count > 0)
            {
                this.EnsureCapacity(this._size + count);
                if (index < this._size)
                {
                    Array.Copy(this._items, index, this._items, index + count, this._size - index);
                }
                c.CopyTo(this._items, index);
                this._size += count;
            }
        }

        /// <summary>
        /// Checks if the given index is in the range of the collection.
        /// </summary>
        /// <param name="index">An integer representing an index of the collection to be checked if it is valid.</param>
        /// <returns>True if the index is a valid collection index.</returns>
        public bool IsValidIndex(int index)
        {
            return ((index >= 0) && (index < this._size));
        }

        /// <summary>
        /// Get the last object of the collection.
        /// </summary>
        /// <returns>The last object of the collection.</returns>
        public T last()
        {
            if (this.Count == 0)
            {
                return default(T);
            }
            return this[this.Count - 1];
        }

        /// <summary>
        /// Get the last object in the collection and changes the current index equal to the size-1 of the collection.
        /// </summary>
        /// <returns>The last object of the collection.</returns>
        public object Last()
        {
            if (this.Count == 0)
            {
                return null;
            }
            this.pos = this.Count - 1;
            return this[this.pos];
        }

        /// <summary>
        /// Internally used to speed up Swap method when multiple swap operations must be performed (more than 100). ClearIndexDictionary() method must be called at the end of the swap operations.
        /// </summary>
        public void MakeIndexDictionary()
        {
            this.mSwapDictionary = new Dictionary<T, int>();
            int num = 0;
            foreach (T local in this)
            {
                this.mSwapDictionary.Add(local, num);
                num++;
            }
        }

        /// <summary>
        /// Get the next object of the collection and also increases the current index of the collection.
        /// </summary>
        /// <returns>The next object of the collection.</returns>
        public object Next()
        {
            if ((this.pos + 1) >= this.Count)
            {
                return null;
            }
            this.pos++;
            return this[this.pos];
        }

        /// <summary>
        /// Get the previus object of the collection and also decreases the current index of the collection.
        /// </summary>
        /// <returns>The previus object of the collection.</returns>
        /// <remarks>If the collection reaches the end returns null.</remarks>
        public object Previous()
        {
            if ((this.pos - 1) <= 0)
            {
                return null;
            }
            this.pos--;
            return this[this.pos];
        }

        /// <summary>
        /// Removes all objects from the collection.
        /// </summary>
        public virtual void RemoveAll()
        {
            Array.Clear(this._items, 0, this._size);
            this._size = 0;
        }

        /// <summary>
        /// Removes an object from a specified index of the collection.
        /// </summary>
        /// <param name="index">The index of the object to be removed.</param>
        public virtual void RemoveAt(int index)
        {
            if (!this.IsValidIndex(index))
            {
                throw new ArgumentOutOfRangeException("vdArray:((index < 0) || (index >= this._size))");
            }
            this._size--;
            if (index < this._size)
            {
                Array.Copy(this._items, index + 1, this._items, index, this._size - index);
            }
            this._items[this._size] = default(T);
        }

        /// <summary>
        /// Removes a specified object from the list.
        /// </summary>
        /// <param name="Object">The object to be removed from the collection.</param>
        /// <returns>True if the object was removed and false if the object was not found.</returns>
        public bool RemoveItem(T Object)
        {
            int objectRealPosition = this.GetObjectRealPosition(Object);
            if (objectRealPosition == -1)
            {
                return false;
            }
            this.RemoveAt(objectRealPosition);
            return true;
        }

        /// <summary>
        /// Removes the last object of the collection.
        /// </summary>
        public void RemoveLast()
        {
            this.RemoveAt(this.Count - 1);
        }

        /// <summary>
        /// Changes the order of the collection.
        /// </summary>
        public virtual void Reverse()
        {
            this.Reverse(0, this.Count);
        }

        /// <summary>
        /// Reverses the sequence of the elements in a range of the ellements starting from a given index.
        /// </summary>
        /// <param name="index">The starting index of the reverse operation.</param>
        /// <param name="count">The number of items to be reversed.</param>
        private void Reverse(int index, int count)
        {
            if ((index < 0) || (count < 0))
            {
                throw new ArgumentOutOfRangeException(" vdArray:(index < 0 || count < 0)");
            }
            if ((this._size - index) < count)
            {
                throw new ArgumentException("vdArray:(_size - index < count)");
            }
            Array.Reverse(this._items, index, count);
        }

        ///// <summary>
        ///// This Function is called when saving the vdArray object to vdml format.
        ///// </summary>
        ///// <param name="serializer">The Serializer object.</param>
        ///// <seealso cref="T:VectorDraw.Serialize.Serializer" />
        //public void Serialize(Serializer serializer)
        //{
        //    serializer.Serialize(this.Count, "_Count");
        //    foreach (object obj2 in this)
        //    {
        //        serializer.Serialize(obj2, "_Item");
        //    }
        //}

        /// <summary>
        /// Set the current idex of the collection.
        /// </summary>
        /// <param name="index">An integer value representing the zero based index of the collection(less than the collection's size).</param>
        public void SetCurrentIndex(int index)
        {
            this.pos = index;
        }

        /// <summary>
        /// Change the current position of the collection to the index of a given object.
        /// </summary>
        /// <param name="entity">An existing object in the collection.</param>
        /// <returns>True if the object was found and the index was changed.</returns>
        /// <remarks>If the collection reaches the start returns null.</remarks>
        public bool SetPosition(object entity)
        {
            int num = 0;
            foreach (object obj2 in this)
            {
                if (object.ReferenceEquals(obj2, entity))
                {
                    this.pos = num;
                    return true;
                }
                num++;
            }
            this.pos = 0;
            return false;
        }

        /// <summary>
        /// Get the first object in the collection and changes the current index to 0.
        /// </summary>
        /// <returns>The first object of the collection.</returns>
        public object Start()
        {
            if (this.Count == 0)
            {
                return null;
            }
            this.pos = 0;
            return this[this.pos];
        }

        /// <summary>
        /// Swaps the position of two specified object in the list.
        /// </summary>
        /// <param name="Object1">First object required.</param>
        /// <param name="Object2">Second object required.</param>
        /// <returns>True if the operation was succesfull.</returns>
        public bool Swap(T Object1, T Object2)
        {
            if ((Object1 == null) || (Object2 == null))
            {
                return false;
            }
            if (!object.ReferenceEquals(Object1, Object2))
            {
                int objectRealPosition = -1;
                int num2 = -1;
                objectRealPosition = this.GetObjectRealPosition(Object1);
                if (objectRealPosition < 0)
                {
                    return false;
                }
                num2 = this.GetObjectRealPosition(Object2);
                if (num2 < 0)
                {
                    return false;
                }
                this[objectRealPosition] = Object2;
                this[num2] = Object1;
                if (this.mSwapDictionary != null)
                {
                    if (this.mSwapDictionary.ContainsKey(Object1))
                    {
                        this.mSwapDictionary.Remove(Object1);
                        this.mSwapDictionary.Add(Object1, num2);
                    }
                    if (this.mSwapDictionary.ContainsKey(Object2))
                    {
                        this.mSwapDictionary.Remove(Object2);
                        this.mSwapDictionary.Add(Object2, objectRealPosition);
                    }
                }
            }
            return true;
        }

        /// <summary>
        /// Gets a System.String that represents the element count of vdArray object.
        /// </summary>
        public override string ToString()
        {
            return (this.Count.ToString() + " Items");
        }

        /// <summary>
        /// Get the items of the collection in a returned array.
        /// </summary>
        public T[] ArrayItems
        {
            get
            {
                return this._items;
            }
        }

        private int Capacity
        {
            get
            {
                return this._items.Length;
            }
            set
            {
                if (value != this._items.Length)
                {
                    if (value < this._size)
                    {
                        throw new ArgumentOutOfRangeException("vdArray:value < _size");
                    }
                    if (value > 0)
                    {
                        T[] destinationArray = new T[value];
                        if (this._size > 0)
                        {
                            Array.Copy(this._items, 0, destinationArray, 0, this._size);
                        }
                        this._items = destinationArray;
                    }
                    else
                    {
                        this._items = new T[0x10];
                    }
                }
            }
        }

        /// <summary>
        /// Get the number of items in the collection.
        /// </summary>
        public virtual int Count
        {
            get
            {
                return this._size;
            }
        }

        /// <summary>
        /// Get the current object in the collection.
        /// </summary>
        public object Current
        {
            get
            {
                return this[this.pos];
            }
        }

        /// <summary>
        /// Get the current index of the collection.
        /// </summary>
        public int CurrentIndex
        {
            get
            {
                return this.pos;
            }
        }

        /// <summary>
        /// Get/Set the object at a specified index of the collection.
        /// </summary>
        /// <param name="index">The index of the object.</param>
        /// <returns>The object at the specified index.</returns>
        public virtual T this[int index]
        {
            get
            {
                if (!this.IsValidIndex(index))
                {
                    throw new ArgumentOutOfRangeException("vdArray:(index < 0 || index >= _size)");
                }
                return this._items[index];
            }
            set
            {
                if (!this.IsValidIndex(index))
                {
                    throw new ArgumentOutOfRangeException("vdArray:(index < 0 || index >= _size)");
                }
                this._items[index] = value;
            }
        }

        [StructLayout(LayoutKind.Sequential)]
        internal struct Enumerator : IEnumerator
        {
            private DictionaryArray<T> list;
            private T[] items;
            private int index;
            private int endIndex;
            internal Enumerator(DictionaryArray<T> list)
            {
                this.list = list;
                this.items = list._items;
                this.index = -1;
                this.endIndex = list._size - 1;
            }

            public bool MoveNext()
            {
                if (this.index == this.endIndex)
                {
                    return false;
                }
                this.index++;
                return true;
            }

            void IEnumerator.Reset()
            {
                this.index = -1;
            }

            public T Current
            {
                get
                {
                    return this.items[this.index];
                }
            }
            object IEnumerator.Current
            {
                get
                {
                    return this.items[this.index];
                }
            }
        }
    }
}
